<?php

namespace App\Services;

use App\Models\SysConfig;
use App\Models\WxCardBase;
use App\Models\WxCardReceive;

/**
 * 微信卡券服务类
 * @package App\Services
 */
class WxCardService
{
    const CARD_TYPE_MEMBER_CARD = 'MEMBER_CARD';//会员卡
    const CARD__TYPE_GROUPON = 'GROUPON';//团购券
    const CARD_TYPE_DISCOUNT = 'DISCOUNT';//折扣券
    const CARD_TYPE_GIFT = 'GIFT';//礼品券
    const CARD_TYPE_CASH = 'CASH';//代金券
    const CARD_TYPE_GENERAL_COUPON = 'GENERAL_COUPON';//通用券
    const CARD_TYPE_GENERAL_CARD = 'GENERAL_CARD';//礼品卡
    const CARD_TYPE_SCENIC_TICKET = 'SCENIC_TICKET';//景点门票
    const CARD_TYPE_MOVIE_TICKET = 'MOVIE_TICKET';//电影票
    const CARD_TYPR_BOARDING_PASS = 'BOARDING_PASS';//飞机票
    const CARD_TYPE_MEETING_TICKET = 'MEETING_TICKET';//会议门票
    const CARD_TYPR_BUS_TICKET = 'BUS_TICKET';//汽车票

    //码型
    const CODE_TYPE_TEXT = 'CODE_TYPE_TEXT';//文本
    const CODE_TYPE_BARCODE = 'CODE_TYPE_BARCODE';//一维码
    const CODE_TYPE_QRCODE = 'CODE_TYPE_QRCODE';//二维码
    const CODE_TYPE_ONLY_QRCODE = 'CODE_TYPE_ONLY_QRCODE';//二维码无code显示
    const CODE_TYPE_ONLY_BARCODE = 'CODE_TYPE_ONLY_BARCODE';//一维码无code显示
    const CODE_TYPE_NONE = 'CODE_TYPE_NONE';//不显示code和条形码

    //有效期时间类型
    const DATE_TYPE_FIX_TIME_RANGE = 'DATE_TYPE_FIX_TIME_RANGE';//固定日期区间
    const DATE_TYPE_FIX_TERM = 'DATE_TYPE_FIX_TERM';//固定时长(自领取后按天算)

    //code码生成类别
    const CODE_MODEL_DEFINE = 'USE_CUSTOM_CODE';

    //卡券状态
    const CARD_STATUS_NOPUBLISH = 0;//待发布
    const CARD_STATUS_NOT_VERIFY = 10;//待审核
    const CARD_STATUS_VERIFY_FAIL = 20;//审核失败
    const CARD_STATUS_VERIFY_OK = 30;//通过审核
    const CARD_STATUS_USER_DELETE = 40;//卡券被商户删除
    const CARD_STATUS_DISPATCH = 50;//在公众平台投放过的卡券


    protected $cardTypeObjList;//卡券类型对象列表
    protected $cardTypeList;//卡券类型列表
    protected $codeTypeList;//码型列表
    protected $dateTypeList;//有效期时间类型列表
    protected $cardStatusList;//卡券状态列表

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->cardTypeObjList = [
            self::CARD_TYPE_MEMBER_CARD => __NAMESPACE__ . '\WxCard\WxCardMemberCard',//会员卡
            self::CARD_TYPE_CASH => __NAMESPACE__ . '\WxCard\WxCardCash',//代金券
            self::CARD_TYPE_DISCOUNT => __NAMESPACE__ . '\WxCard\WxCardDiscount',//折扣券
            self::CARD_TYPE_GENERAL_COUPON => __NAMESPACE__ . '\WxCard\WxCardGeneralCoupon',//优惠券
            self::CARD_TYPE_GIFT => __NAMESPACE__ . '\WxCard\WxCardGift',//兑换券
            self::CARD__TYPE_GROUPON => __NAMESPACE__ . '\WxCard\WxCardGroupon',//团购券
        ];
        $this->cardTypeList = [
            self::CARD_TYPE_MEMBER_CARD => '会员卡',
            self::CARD_TYPE_CASH => '代金券',
            self::CARD_TYPE_DISCOUNT => '折扣券',
            self::CARD_TYPE_GENERAL_COUPON => '优惠券',
            self::CARD_TYPE_GIFT => '兑换券',
            self::CARD__TYPE_GROUPON => '团购券',
        ];
        //码型列表
        $this->codeTypeList = [
            ['type' => self::CODE_TYPE_TEXT, 'name' => '文本'],
            ['type' => self::CODE_TYPE_BARCODE, 'name' => '一维码'],
            ['type' => self::CODE_TYPE_QRCODE, 'name' => '二维码'],
            ['type' => self::CODE_TYPE_ONLY_QRCODE, 'name' => '二维码无code显示'],
            ['type' => self::CODE_TYPE_ONLY_BARCODE, 'name' => '一维码无code显示'],
            ['type' => self::CODE_TYPE_NONE, 'name' => '不显示code和条形码'],
        ];
        //有效期时间类型列表
        $this->dateTypeList = [
            ['type' => self::DATE_TYPE_FIX_TIME_RANGE, 'name' => '固定日期区间'],
            ['type' => self::DATE_TYPE_FIX_TERM, 'name' => '固定时长(自领取后按天算)'],
        ];
        //卡券状态列表
        $this->cardStatusList = [
            self::CARD_STATUS_NOPUBLISH => '待发布',
            self::CARD_STATUS_NOT_VERIFY => '待审核',
            self::CARD_STATUS_VERIFY_FAIL => '审核失败',
            self::CARD_STATUS_VERIFY_OK => '通过审核',
            self::CARD_STATUS_USER_DELETE => '卡券被商户删除',
            self::CARD_STATUS_DISPATCH => '在公众平台投放过的卡券',
        ];
    }

    /**
     * 获取卡券类别名称列表
     * @return array
     */
    public function getTypeNameList()
    {
        $cardTypeNameList = [];
        if ($this->cardTypeList) {
            foreach ($this->cardTypeList as $typeKey => $cardName) {
                $cardTypeNameList[] = ['type' => $typeKey, 'name' => $cardName];
            }
        }
        return $cardTypeNameList;
    }

    /**
     * 获取卡券类型名称
     * @param $type
     * @return mixed|string
     */
    public function getTypeNameStr($type)
    {
        return $this->cardTypeList[$type] ?? '';
    }

    /**
     * 获取卡券标识分组列表
     * @return array
     */
    public function getCardKeyGroupList()
    {
        $list = [];
        foreach ($this->cardTypeObjList as $type => $obj){
            $list[$type] = (new $obj)->getCardKeyList();
        }
        return $list;
    }
    /**
     * 获取卡券状态
     * @param $status
     * @return mixed|string
     */
    public function getCardStatusStr($status)
    {
        return $this->cardStatusList[$status] ?? '未知';
    }

    /**
     * 码型
     * @return array
     */
    public function getCodeTypeList()
    {
        return $this->codeTypeList;
    }

    /**
     * 有效期时间类型
     * @return array
     */
    public function getDateTypeList()
    {
        return $this->dateTypeList;
    }

    /**
     * 获取卡券测试白名单
     * @return array|mixed|null
     * @throws \Exception
     */
    public function getTestWhitelist()
    {
        $whiteList = (new SysConfigService())->get(SysConfigService::WX_CARD_TEST_WHITE_LIST);
        if (empty($whiteList)) {
            $whiteList = [];
        }
        return $whiteList;
    }

    /**
     * 设置卡券测试白名单
     * @param $openIds
     * @return mixed
     * @throws \Exception
     */
    public function setTestWhitelist($openIds)
    {
        if (empty($openIds)) {
            throw new \Exception('请提供要设置卡券测试白名单的openid列表');
        }
        $app = EasyWechat::officialAccount();
        $apiRs = $app->card->setTestWhitelist($openIds);
        //校验微信错误
        WxErrorService::checkApiResponse($apiRs);
        return (new SysConfigService())->set(SysConfigService::WX_CARD_TEST_WHITE_LIST, $openIds);
    }

    /**
     * 微信卡券颜色列表
     * @return array
     * @throws \Exception
     */
    public function getColors()
    {
        $sysConfigSer = new SysConfigService();
        $colors = $sysConfigSer->get(SysConfigService::WX_CARD_COLORS);
        if (empty($colors)) {
            $app = EasyWechat::officialAccount();
            $apiRs = $app->card->colors();
            //校验微信错误
            WxErrorService::checkApiResponse($apiRs);
            $colors = $apiRs['colors'];
            $sysConfigSer->set(SysConfigService::WX_CARD_COLORS, $colors);
        }
        return $colors;
    }

    /**
     * 通过颜色值转换为颜色名称，如果获取不到则直接原样返回
     * @param $colorValue
     * @return mixed|string
     * @throws \Exception
     */
    public function getColorNameByValue($colorValue)
    {
        $valueToName = [];
        $colors = $this->getColors();
        if (!empty($colors)) {
            foreach ($colors as $v) {
                $valueToName[$v['value']] = $v['name'];
            }
        }
        return isset($valueToName[$colorValue]) ? $valueToName[$colorValue] : $colorValue;
    }

    /**
     * 获取卡券类目列表
     * @return array
     * @throws \Exception
     */
    public function getCategories()
    {
        $sysConfigSer = new SysConfigService();
        $categories = $sysConfigSer->get(SysConfigService::WX_CARD_CATEGORIES);
        if (empty($categories)) {
            $app = EasyWechat::officialAccount();
            $apiRs = $app->card->categories();
            //校验微信错误
            WxErrorService::checkApiResponse($apiRs);
            $categories = $apiRs['category'];
            $sysConfigSer->set(SysConfigService::WX_CARD_CATEGORIES, $categories);
        }
        return $categories;
    }

    /**
     * 获取指定类别的卡券列表
     * @param $cardType
     * @return array
     */
    public function getCardListByType($cardType)
    {
        $cardList = WxCardBase::where('card_type', $cardType)->orderBy('card_base_id', 'ASC')->get();
        return $cardList ? $cardList->toArray() : [];
    }

    /**
     * 通过微信卡券ID获取卡券数据
     * @param $cardId
     * @return array
     */
    public function getCardByCardId($cardId)
    {
        $cardBase = WxCardBase::where('card_id', $cardId)->first();
        return $cardBase ? $cardBase->toArray() : [];
    }

    /**
     * 创建卡券对象
     * @param $type
     * @return WxCard\WxCardAbstract
     */
    public function createCardObj($type)
    {
        if (!array_key_exists($type, $this->cardTypeObjList)) {
            throw new \InvalidArgumentException("$type 无效的卡券类型");
        }
        return new $this->cardTypeObjList[$type];
    }

    /**
     * 从接口获取所有卡券列表
     * @return array
     * @throws \Exception
     */
    public function getAllApiCardList()
    {
        $cardIdList = [];
        $pageOffset = 0;
        $pageSize = 50;
        $total = 0;
        $statusList = ['CARD_STATUS_NOT_VERIFY', 'CARD_STATUS_VERIFY_FAIL', 'CARD_STATUS_VERIFY_OK', 'CARD_STATUS_DELETE', 'CARD_STATUS_DISPATCH'];//状态列表
        $app = EasyWechat::officialAccount();
        do {
            $list = $app->card->list($pageOffset, $pageSize, $statusList);
            WxErrorService::checkApiResponse($list);
            if (!empty($list['card_id_list'])) {
                $cardIdList = array_merge($cardIdList, (array)$list['card_id_list']);
            }
            $itemPageCount = !empty($list['card_id_list']) ? count((array)$list['card_id_list']) : 0;
            $itemTotalCount = !empty($list['total_num']) ? $list['total_num'] : 0;
            $pageOffset += $itemPageCount;
            $total += $itemPageCount;
        } while ($itemPageCount == $pageSize && $total < $itemTotalCount);

        return $cardIdList;
    }

    /**
     * 同步线上卡券数据
     * @return bool
     * @throws \Exception
     */
    public function sync()
    {
        $cardIdList = $this->getAllApiCardList();
        $app = EasyWechat::officialAccount();
        foreach ($cardIdList as $status => $cardId) {
            $cardInfo = $app->card->get($cardId);
            if (!empty($cardInfo['card'])) {
                $cardType = $cardInfo['card']['card_type'];
                /*echo '<pre>';
                print_r($cardInfo['card']);
                echo '</pre>';*/
                //$this->createCardObj($cardType)->sync($cardInfo['card']);
                \App\Jobs\WxCardSyncOneCard::dispatch($cardType, $cardInfo['card'])->onQueue('default');
            }
        }
        return true;
    }

    /**
     * 卡券删除
     * @param $cardBaseId
     * @return bool
     * @throws \Exception
     */
    public function destory($cardBaseId)
    {
        \DB::beginTransaction();
        try {
            $cardModel = WxCardBase::findOrFail($cardBaseId);
            //已经发布了的
            if ($cardModel->audit_status != self::CARD_STATUS_NOPUBLISH && !empty($cardModel->card_id)) {
                $app = EasyWechat::officialAccount();
                $apiRs = $app->card->delete($cardModel->card_id);
                WxErrorService::checkApiResponse($apiRs);
            }
            $cardModel->audit_status = self::CARD_STATUS_USER_DELETE;
            $cardModel->save();
            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            throw new \Exception($e->getMessage());
        }
        return true;
    }

    /**
     * 调整卡券库存
     * @param $cardBaseId
     * @param $stockNum
     * @param string $modType
     * @return bool
     * @throws \Exception
     */
    public function modifyStock($cardBaseId, $stockNum, $modType = 'increase')
    {
        if (!in_array($modType, ['increase', 'reduce'])) {
            throw new \Exception('库存操作类型有误');
        }
        \DB::beginTransaction();
        try {
            $cardModel = WxCardBase::findOrFail($cardBaseId);
            $sku = $cardModel->sku;
            //已经发布了的
            $app = EasyWechat::officialAccount();
            if ($modType == 'increase') {
                $apiRs = $app->card->increaseStock($cardModel->card_id, $stockNum);
                $sku['quantity'] = $sku['quantity'] + $stockNum;
                $sku['total_quantity'] = $sku['total_quantity'] + $stockNum;
            } elseif ($modType == 'reduce') {
                if ($cardModel->sku['total_quantity'] < $stockNum) {
                    throw new \Exception('您要减少的库存数超过当前总库存');
                }
                $apiRs = $app->card->reduceStock($cardModel->card_id, $stockNum);

                $sku['quantity'] = $sku['quantity'] - $stockNum;
                $sku['total_quantity'] = $sku['total_quantity'] - $stockNum;
            }
            WxErrorService::checkApiResponse($apiRs);
            //更新卡券库存数
            $cardModel->sku = $sku;
            $cardModel->save();
            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            throw new \Exception($e->getMessage());
        }
        return true;
    }

    /**
     * 更改Code
     * 为确保转赠后的安全性，微信允许自定义Code的商户对已下发的code进行更改。
     * 注：为避免用户疑惑，建议仅在发生转赠行为后（发生转赠后，微信会通过事件推送的方式告知商户被转赠的卡券Code）对用户的Code进行更改。
     * @param $receiveId
     * @param $newCode
     * @return bool
     * @throws \Exception
     */
    public function updateCode($receiveId, $newCode)
    {
        \DB::beginTransaction();
        try {
            $cardRecModel = WxCardReceive::findOrFail($receiveId);
            //调用接口更改code
            $app = EasyWechat::officialAccount();
            $apiRs = $app->card->code->update($cardRecModel->user_card_code, $newCode, $cardRecModel->card_id);
            WxErrorService::checkApiResponse($apiRs);

            $cardRecModel->user_card_code = $newCode;
            $cardRecModel->save();
            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            throw new \Exception($e->getMessage());
        }
        return true;
    }

    /**
     * 标记已领卡券为失效
     * 为满足改票、退款等异常情况，可调用卡券失效接口将用户的卡券设置为失效状态。
     * @param $receiveId
     * @return bool
     * @throws \Exception
     */
    public function disable($receiveId)
    {
        \DB::beginTransaction();
        try {
            $cardRecModel = WxCardReceive::findOrFail($receiveId);
            //调用接口标记为失效
            if (!empty($cardRecModel->card_id) && !empty($cardRecModel->user_card_code)) {
                $app = EasyWechat::officialAccount();
                $apiRs = $app->card->code->disable($cardRecModel->user_card_code, $cardRecModel->card_id);
                WxErrorService::checkApiResponse($apiRs);
            }
            $cardRecModel->delete();
            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            throw new \Exception($e->getMessage());
        }
        return true;
    }
}